﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using IntelligentDecisioning.SharePoint.Helpers;
using IntelligentDecisioning.SharePoint.Definition;
using IntelligentDecisioning.SharePoint.Logging;
using IntelligentDecisioning.SharePoint.Definition.Fields;
using System.Security.Permissions;
using Microsoft.SharePoint.Security;
using Microsoft.Practices.SPG.Common.ServiceLocation;
using Microsoft.Practices.SPG.Common.Logging;

namespace IntelligentDecisioning.SharePoint.FeatureReceivers
{
    /// <summary>
    /// Intelligent Decisioning Base Feature Receiver.
    /// Provides the core logic and helpers for all feature receivers.
    /// This class provides methods for creating:
    /// 
    ///  - Site columns
    ///  - Lists
    ///  - Event Receivers
    ///  - Content types
    ///  - Security Groups
    /// 
    /// This class registers each new feature with the Intelligent Decisioning 
    /// feature configuration framework
    /// 
    /// When creating a new feature, extend this class and call the base class methods 
    /// to perform all the "heavy lifting" required to install and activiate the 
    /// specfic feature functionality. 
    /// 
    /// This class seals the sharepoint feature activating / deactivating methods 
    /// and provides custom extension points in the DoFeatureActivating() methods etc.
    /// Developers of custom feature installers should call these methods to ensure
    /// that their code is called at the correct time in the feature activation process
    /// and that the resources used during feature activation are cleaned up correctly.
    /// </summary>
    [SharePointPermission(SecurityAction.LinkDemand, ObjectModel = true)]
    public abstract class IDBaseFeatureReceiver: SPFeatureReceiver
    {
        #region Data
        
        protected SPSite SiteCollection { get; private set; }
        protected SPWeb RootWeb { get; private set; }
        protected SPWeb CurrentWeb { get; private set; }
        protected String _featureName;
        protected Guid _featureId;
        protected SPFeatureReceiverProperties _properties;
      

        #endregion

        #region Helpers

        // We delegate most of our work to the various
        // IDHelper classes so that the same functionality
        // can be reused outside the feature activtation framework
        // e.g. In custom user controls, webparts, admin utilities etc
        protected IDListHelper _listHelper;
        protected IDContentTypeHelper _contentTypeHelper;
        protected IDSecurityHelper _securityHelper;
        protected IDSiteColumnHelper _siteColumnHelper;


        #endregion

        #region Configuration Properties

        public bool HasLists { get; set; }
        public bool HasCustomContent { get; set; }
        public bool HasDocumentLibraries { get; set; }
        public bool HasSecurityGroups { get; set; }
        public bool HasContentTypes { get; set; }
        public bool HasSiteColumns { get; set; }
        public bool HasWorkflows { get; set; }
        public bool HasEventHandlers { get; set; }
        public bool HasCustomMasterPage { get; set; }

        protected abstract void DefineFeatureProperties();

        #endregion

        #region Logging

        protected FeatureInstallationLog _installLog;

        #endregion

        #region Constructor

        public IDBaseFeatureReceiver()
        {
           
        }

        private void SetDefaults()
        {
            HasContentTypes = false;
            HasCustomContent = false;
            HasDocumentLibraries = false;
            HasEventHandlers = false;
            HasSecurityGroups = false;
            HasWorkflows = false;
            HasLists = false;
            HasSiteColumns = false;
        }

        #endregion

        #region Base Class Overrides - Activated, Deactivated, Installed, Uninstalled

        /// <summary>
        /// Provides an activation framework for the Intelligent Decisioning features.
        /// 
        /// Feature content is provisioned in a  predefined logical order.
        /// </summary>
        /// <param name="properties"></param>
        public sealed override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            // TODO: record the progress through the installation and attempt to clean up if something bad happens

            try
            {
                // Initialise this feature receiver
                InitialiseFeatureReceiver(properties);

                // Adds any security groups required by the feature
                CreateSecurityGroups();

                // Adds an site columns required by the feature
                CreateSiteColumns();

                // Creates feature specfic content types
                CreateContentTypes();

                // Creates the lists required by the feature
                CreateLists();

                // Creates the document librbaries required by the feature
                CreateDocumentLibraries();

                // Creates  workflow libraries
                CreateWorkflowAssociations();

                // Addes code handlerw to lists, document librbaries etc
                ConnectEventHandlers();

                // Apply custom branding is included in this feature
                ApplyCustomBranding();

                // Adds any custom content required by this feature
                AddCustomContent();

                // Allows the extending class to perform custom activation if required
                DoFeatureActivated();

                // Notify the site administrators that the new feature has been activated.
                NotifySiteAdministrators();

            }
            catch(Exception e)
            {
                // At the point of the exception we are unable to use our feature installation
                // log reference as no further changes are comitted through its SPWeb instance
                // so we need to create another one to log the error
                using (SPSite site = new SPSite(this.SiteCollection.Url))
                {
                    _installLog = new FeatureInstallationLog(_featureName, _featureId, site);
                    _installLog.WriteToLog("Unexpected Error installing feature: ", e);
                    _installLog.WriteToLog(e.StackTrace);
                }
                
                String errMsg = "Exception details are - " + e.Message;
                while (e.InnerException != null)
                {
                    errMsg += " Inner exception message: " + e.InnerException.Message;
                    e = e.InnerException;
                }

                SharePointServiceLocator.Current.GetInstance<ILogger>().LogError(errMsg);

                throw new Exception("Unexpected error while activating feature. Please check the feature installation log for more details");
            }
        }

    

        private void NotifySiteAdministrators()
        {
            String subject = String.Format("ID-SharePoint Notification: {0} Activated sucessfully", _featureName);
            String message = "When I've got more time I'll code up what happened from the log";
            _securityHelper.NotifySiteAdministrators(subject, message);
        }

       
        

        public sealed override void FeatureDeactivating(SPFeatureReceiverProperties properties)
        {
            try
            {
                InitialiseFeatureReceiver(properties);

                // Allow any custom cleanup code to run first 
                DoFeatureDeactivating();

                // Remove custom content

                // Disconnect event handlers

                // Remove workflow associations

                // Remove document libraries

                // Remove lists

                // Remove Content Types

                // Remove Site Columns

                // Remove Security Groups
            }
            catch { }
        }

        

        public sealed override void FeatureInstalled(SPFeatureReceiverProperties properties)
        {
            DoFeatureInstalled();
        }

        

        public sealed override void FeatureUninstalling(SPFeatureReceiverProperties properties)
        {
            InitialiseFeatureReceiver(properties);
            DoFeatureUninstalling();
        }

        #endregion

        #region Custom Extension Points, DoFeatureActiviated etc...

        protected virtual void DoFeatureActivated()
        {
            //
        }

        protected virtual void DoFeatureDeactivating()
        {
            //
        }

        protected virtual void DoFeatureInstalled()
        {
            //
        }

        protected virtual void DoFeatureUninstalling()
        {
            //
        }

        #endregion

        #region Feature Framework Implementation

        #region Initialise

        private void InitialiseFeatureReceiver(SPFeatureReceiverProperties properties)
        {
            if (properties == null) throw new ArgumentNullException("SPFeatureReceiverProperties");

            _properties = properties;
            _featureId = properties.Definition.Id;
            _featureName = properties.Definition.Name;
            
            switch (properties.Definition.Scope)
            {
                case SPFeatureScope.Farm:
                //    _installLog.WriteToLog("Feature is scoped at Farm level");
                    throw new NotSupportedException("Farm scoped features not yet implemented in the id-sharepoint framework");
                   // break;
         
                case SPFeatureScope.ScopeInvalid:
                    break;
                
                case SPFeatureScope.Site:
                    {
                        SiteCollection = (SPSite)properties.Feature.Parent;
                        RootWeb = SiteCollection.RootWeb;
                        CurrentWeb = RootWeb;
                        _installLog = new FeatureInstallationLog(_featureName, _featureId, SiteCollection);
                        _installLog.WriteToLog("Activating feature");
                        _installLog.WriteToLog("Feature is scoped at SiteCollection level");

                        break;
                    }
                
                case SPFeatureScope.Web:
                    {
                        CurrentWeb = properties.Feature.Parent as SPWeb;
                        SiteCollection = CurrentWeb.Site;
                        RootWeb = CurrentWeb.Site.RootWeb;
                        _installLog = new FeatureInstallationLog(_featureName, _featureId, SiteCollection);
                        _installLog.WriteToLog("Activating feature");
                        _installLog.WriteToLog("Feature is scoped at Site level");

                        break;
                    }
                
                case SPFeatureScope.WebApplication:
                  //  _installLog.WriteToLog("Feature is scoped at Web Application level");
                    throw new NotSupportedException("Web application scoped featues not implemented in framework");
                   // break;
                
                default:
                    break;
            }

         
            _contentTypeHelper = new IDContentTypeHelper(SiteCollection, CurrentWeb, _installLog);
            _listHelper = new IDListHelper(SiteCollection,CurrentWeb, _installLog);
            _securityHelper = new IDSecurityHelper(SiteCollection,CurrentWeb, _installLog);
            _siteColumnHelper = new IDSiteColumnHelper(SiteCollection, CurrentWeb, _installLog);


            SetDefaults();
            // Implementing classes use this method to 
            // define the functionality provisioned for this feature
            DefineFeatureProperties();
            
        }

        #endregion

        #region AddCustomContent

        private void AddCustomContent()
        {
            if (HasCustomContent)
            {
                
            }
        }

        #endregion

        #region Apply Custom Branding

        private void ApplyCustomBranding()
        {
            if (HasCustomMasterPage)
            {
                ApplyCustomMasterPage(GetCustomMasterPage());
            }
        }

        /// <summary>
        /// Ovveride this method to specify the name of the custom master page to be applied
        /// </summary>
        /// <returns></returns>
        protected virtual String GetCustomMasterPage()
        {
            return "default.master";
        }


        private void ApplyCustomMasterPage(String pageName)
        {    
            SPSecurity.RunWithElevatedPrivileges(delegate() 
            {
                // Apply the study site master page
                string masterUrlPath = CurrentWeb.ServerRelativeUrl;
                if (!masterUrlPath.EndsWith(@"/")) masterUrlPath += @"/";
                masterUrlPath += @"_catalogs/masterpage/" + pageName;
                CurrentWeb.MasterUrl = masterUrlPath;
                CurrentWeb.Update();
            });
        }

        #endregion

        #region Connect Event Handlers

        private void ConnectEventHandlers()
        {
            if (HasEventHandlers)
            {
                _listHelper.ProvisionListEventHandlers(GetListEventHandlers());
            }
        }

        #endregion

        #region Create Workflow Associations

        private void CreateWorkflowAssociations()
        {
            if (HasWorkflows)
            {

            }
        }

        #endregion

        #region Create Document Libraries

        private void CreateDocumentLibraries()
        {
            if (HasDocumentLibraries)
            {
                _listHelper.ProvisionLists(GetLists());
            }
        }

        #endregion

        #region Create Lists

        private void CreateLists()
        {
            if (HasLists)
            {
                _listHelper.ProvisionLists(GetLists());
            }
        }

        #endregion

        #region Create Content Types

        private void CreateContentTypes()
        {
            if (HasContentTypes)
            {
                _contentTypeHelper.ProvisionContentTypes(GetContentTypes());
            }
        }

       
       

        #endregion

        #region Create Site Columns

        private void CreateSiteColumns()
        {
            _installLog.WriteToLog("Checking for site columns");

            if (HasSiteColumns)
            {        
                List<Field> siteColumns = GetSiteColumns();
                _siteColumnHelper.ProvisionSiteColumns(siteColumns);
            }
        }

        #endregion

        #region Create Security Groups

        private void CreateSecurityGroups()
        {
            _installLog.WriteToLog("Checking for security groups");

            if (HasSecurityGroups)
            {
                List<SecurityGroup> customGroups = GetCustomGroups();
                _securityHelper.ProvisionCustomGroups(customGroups);
            }
            else
            {
                _installLog.WriteToLog("There are no custom security groups to provision");
            }
        }

        /// <summary>
        /// Override this method and add the securiyt groups to provision
        /// </summary>
        /// <returns></returns>
        protected virtual List<SecurityGroup> GetCustomGroups()
        {
            return new List<SecurityGroup>();
        }

        #endregion

        protected virtual List<Field> GetSiteColumns()
        {
            return new List<Field>();
        }

        protected virtual List<SharePointList> GetLists()
        {
            return new List<SharePointList>();
        }

        protected virtual List<ContentType> GetContentTypes()
        {
            return new List<ContentType>();
        }

        protected virtual List<SharePointListItemEventHandler> GetListEventHandlers()
        {
            return new List<SharePointListItemEventHandler>();
        }


        #endregion
    }
}
